package com.ray.service;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.notNullValue;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang.StringUtils;
import org.joda.time.DateTime;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.ray.bean.AttendDailySalaryDetailBean;
import com.ray.bean.AttendOvertimeRuleBean;
import com.ray.bean.AttendanceMonthSummaryBean;
import com.ray.bean.DatatablesViewPageBean;
import com.ray.bean.TableData;
import com.ray.mapper.AttendanceMapper;
import com.ray.mapper.DingTalkMapper;
import com.ray.pojo.attend.AttendBoardPojo;
import com.ray.pojo.attend.AttendPlanAndFinanceDetailPojo;
import com.ray.pojo.attend.AttendanceDailyInfoPojo;
import com.ray.pojo.attend.AttendanceInfoPojo;
import com.ray.pojo.attend.AttendanceInfoResultPojo;
import com.ray.pojo.attend.AttendanceSummaryPojo;
import com.ray.pojo.attend.DailySalaryDetailByConditionPojo;
import com.ray.pojo.attend.DailySalaryDetailPojo;
import com.ray.pojo.attend.DeptAndWorkshopPojo;
import com.ray.pojo.attend.PendingApprovalInfoPojo;
import com.ray.pojo.attend.QuerySummaryBodyWorkdatePojo;
import com.ray.pojo.attend.UserFinancePojo;
import com.ray.pojo.attend.WorkdateClockInfo;
import com.ray.pojo.attend.WorkshopAttendBoardPojo;
import com.ray.pojo.attend.WorkshopAttendPersonNumPojo;
import com.ray.pojo.attend.WorkshopCurrentDayAttendInfoBoardPojo;
import com.ray.pojo.attend.WorkshopFillcardNumPojo;
import com.ray.pojo.attend.WorkshopInfoPojo;
import com.ray.service.model.dorm.PersonOfDormStatisticsOut;
import com.ray.util.AttendanceUtil;
import com.ray.util.DingTalkUtil;
import com.ray.util.StringUtil;
import com.ray.util.TimeAndUnix;

/**
 * 考勤模块
 * @author 王荣
 */
@Service
public class AttendanceService {
	@Autowired
	AttendanceMapper attendanceMapper;
	@Autowired
	DingTalkMapper dingTalkMapper;
	@Autowired
	DingTalkService dingTalkService;
	
	/**
	 * 车间考勤看板
	 * @param requestMap
	 * @return {@code AttendBoardPojo}
	 * @throws ParseException
	 * @author 王荣
	 */
	public AttendBoardPojo getWorkshopAttendanceBoard(Map<String, Object> requestMap) throws ParseException {
		requestMap.put("startDate", new DateTime().dayOfMonth().withMinimumValue().toString("yyyy-MM-dd"));
		if("currentDate".equals(requestMap.get("currentDate"))) {//查看当日
			requestMap.put("startDate", new DateTime().toString("yyyy-MM-dd"));
		}
		requestMap.put("endDate", new DateTime().toString("yyyy-MM-dd"));
		//requestMap.put("startDate", "2019-07-01");
		//requestMap.put("endDate", "2019-07-31");
		// 返回的考勤看板信息
		AttendBoardPojo attendBoard = new AttendBoardPojo();
		//车间月度看板信息
		List<WorkshopAttendBoardPojo> workshopAttendBoardList = attendBoard.getWorkshopAttendBoardList();
		WorkshopAttendBoardPojo workshopAttendBoardPojo = null;
		//考勤统计信息
		List<AttendanceInfoResultPojo> attendanceInfoResultPojos = getAttendanceInfo(requestMap);
		// 车间考勤审批单信息  
		List<WorkshopFillcardNumPojo> workshopFillcardNumPojos = attendanceMapper.getWorkshopFillcardNum(requestMap);
		//今日上班人数查询
		List<WorkshopAttendPersonNumPojo> workshopAttendPersonNumPojos = attendanceMapper.getWorkshopAttendPersonNum(requestMap);
		//次数最多
		int leaveTimesMaximum = 0,lateTimesMaximum = 0,earlyTimesMaximum = 0,fillcardTimesMaximum = 0;
		//时长最长
		long leaveDurationMaximum = 0,lateDurationMaximum = 0,earlyDurationMaximum = 0;
		//遍历每人
		for(int index=0;index<attendanceInfoResultPojos.size();index++) {
			//每人的考勤统计信息
			AttendanceInfoResultPojo attendanceInfoResultPojo = attendanceInfoResultPojos.get(index);
			//补卡次数信息
			WorkshopFillcardNumPojo workshopFillcardNumPojo = workshopFillcardNumPojos.get(index);
			//补卡次数
			int fillcardNum = workshopFillcardNumPojo.getFillcardNum();
			String userName = attendanceInfoResultPojo.getUserName();
			String deptName = attendanceInfoResultPojo.getDeptName();
			String workshopName = attendanceInfoResultPojo.getWorkshopName();
			//如果车间月汇总看板信息为null || 部门!=这个人的部门 ||车间!=这个人的车间
			if(workshopAttendBoardPojo==null 
					|| (deptName!=null&&!deptName.equals(workshopAttendBoardPojo.getDeptName()))
					|| !StringUtil.checkEqu(workshopName, workshopAttendBoardPojo.getWorkshopName())) {
				//新建对象并添加信息
				workshopAttendBoardPojo = new WorkshopAttendBoardPojo(deptName,workshopName);
				workshopAttendBoardList.add(workshopAttendBoardPojo);
			}
			//车间人数+1
			workshopAttendBoardPojo.setWorkshopPresonNum(workshopAttendBoardPojo.getWorkshopPresonNum()+1);
			int lateNum = 0,earlyNum = 0,leaveNum = 0,lackNum = 0;
			long lateDuration = 0,earlyDuration = 0,leaveDuration = 0;
			
			//遍历每天
			for(AttendanceDailyInfoPojo attendanceDailyInfoPojo : attendanceInfoResultPojo.getAttendanceDailyInfoList()) {
				//迟到信息
				List<Map<String, Object>> lateInfos =  attendanceDailyInfoPojo.getLateInfoList();
				if(lateInfos!=null && lateInfos.size()>0) {
					//迟到次数
					lateNum += lateInfos.size();
					for(Map<String, Object> lateInfo : lateInfos) {
						lateDuration += Long.parseLong(lateInfo.get("duration").toString());
						//迟到
						Map<String, Object> returnMap = new HashMap<String, Object>();
						returnMap.put("deptName", attendanceInfoResultPojo.getDeptName());
						returnMap.put("workshopName", attendanceInfoResultPojo.getWorkshopName());
						returnMap.put("userName", attendanceInfoResultPojo.getUserName());
						returnMap.put("workdate", attendanceDailyInfoPojo.getWorkdate());
						returnMap.put("baseTime", lateInfo.get("baseTime").toString());
						attendBoard.getLateTimesPersonName().add(returnMap);
					}
				}
				//早退信息
				List<Map<String, Object>> earlyInfos =  attendanceDailyInfoPojo.getEarlyInfoList();
				if(earlyInfos!=null && earlyInfos.size()>0) {
					earlyNum += earlyInfos.size();
					for(Map<String, Object> earlyInfo : earlyInfos) {
						earlyDuration += Long.parseLong(earlyInfo.get("duration").toString());
						//早退
						Map<String, Object> returnMap = new HashMap<String, Object>();
						returnMap.put("deptName", attendanceInfoResultPojo.getDeptName());
						returnMap.put("workshopName", attendanceInfoResultPojo.getWorkshopName());
						returnMap.put("userName", attendanceInfoResultPojo.getUserName());
						returnMap.put("workdate", attendanceDailyInfoPojo.getWorkdate());
						returnMap.put("baseTime", earlyInfo.get("baseTime").toString());
						attendBoard.getEarlyTimesPersonName().add(returnMap);
					}
				}
				//缺卡信息
				List<Map<String, Object>> lackInfos =  attendanceDailyInfoPojo.getLackInfoList();
				if(lackInfos!=null && lackInfos.size()>0) {
					//缺卡次数
					lackNum += lackInfos.size();
					for(Map<String, Object> lackInfo : lackInfos) {
						//缺卡
						Map<String, Object> returnMap = new HashMap<String, Object>();
						returnMap.put("deptName", attendanceInfoResultPojo.getDeptName());
						returnMap.put("workshopName", attendanceInfoResultPojo.getWorkshopName());
						returnMap.put("userName", attendanceInfoResultPojo.getUserName());
						returnMap.put("workdate", attendanceDailyInfoPojo.getWorkdate());
						returnMap.put("baseTime", lackInfo.get("baseTime").toString());
						attendBoard.getLackTimesPersonName().add(returnMap);
					}
					
				}
				//请假信息
				List<Map<String, Object>> leaveInfos =  attendanceDailyInfoPojo.getLeaveInfoList();
				if(leaveInfos!=null && leaveInfos.size()>0) {
					leaveNum += leaveInfos.size();
					for(Map<String, Object> leaveInfo : leaveInfos) {
						leaveDuration += Long.parseLong(leaveInfo.get("duration").toString());
						//请假
						Map<String, Object> returnMap = new HashMap<String, Object>();
						returnMap.put("deptName", attendanceInfoResultPojo.getDeptName());
						returnMap.put("workshopName", attendanceInfoResultPojo.getWorkshopName());
						returnMap.put("userName", attendanceInfoResultPojo.getUserName());
						returnMap.put("workdate", attendanceDailyInfoPojo.getWorkdate());
						returnMap.put("baseTime", leaveInfo.get("startTime").toString()+"-"+leaveInfo.get("endTime").toString());
						attendBoard.getLeaveTimesPersonName().add(returnMap);
					}
					
				}
			}
			if(lateNum>0) {
				//车间迟到次数
				workshopAttendBoardPojo.setLateNum(workshopAttendBoardPojo.getLateNum()+lateNum);
				//车间迟到人数
				workshopAttendBoardPojo.setLatePresonNum(workshopAttendBoardPojo.getLatePresonNum()+1);
				//迟到次数最多人员
				if(lateNum>=lateTimesMaximum) {
					List<Map<String, Object>> lateTimesMaximumPersons = attendBoard.getLateTimesMaximumPerson();
					lateTimesMaximumPersons = AttendanceUtil.judgeMaximum(lateNum, lateTimesMaximum, lateTimesMaximumPersons, userName);
					lateTimesMaximum = lateNum;
				}
				//迟到时长最多人员
				if(lateDuration>=lateDurationMaximum) {
					List<Map<String, Object>> lateDurationMaximumPersons = attendBoard.getLateDurationMaximumPerson();
					lateDurationMaximumPersons = AttendanceUtil.judgeMaximum(lateDuration, lateDurationMaximum, lateDurationMaximumPersons, userName);
					lateDurationMaximum = lateDuration;
				}
			}
			if(earlyNum>0) {
				//车间早退次数
				workshopAttendBoardPojo.setEarlyNum(workshopAttendBoardPojo.getEarlyNum()+earlyNum);
				//车间早退人数
				workshopAttendBoardPojo.setEarlyPresonNum(workshopAttendBoardPojo.getEarlyPresonNum()+1);
				//早退次数
				if(earlyNum>=earlyTimesMaximum) {
					List<Map<String, Object>> earlyTimesMaximumPersons = attendBoard.getEarlyTimesMaximumPerson();
					earlyTimesMaximumPersons = AttendanceUtil.judgeMaximum(earlyNum, earlyTimesMaximum, earlyTimesMaximumPersons, userName);
					earlyTimesMaximum = earlyNum;
				}
				//早退时长
				if(earlyDuration>=earlyDurationMaximum) {
					List<Map<String, Object>> earlyDurationMaximumPersons = attendBoard.getEarlyDurationMaximumPerson();
					earlyDurationMaximumPersons = AttendanceUtil.judgeMaximum(earlyDuration, earlyDurationMaximum, earlyDurationMaximumPersons, userName);
					earlyDurationMaximum = earlyDuration;
				}
			}
			if(lackNum>0) {
				//车间缺卡次数
				workshopAttendBoardPojo.setAbsentNum(workshopAttendBoardPojo.getAbsentNum()+lackNum);
				//车间缺卡人次
				workshopAttendBoardPojo.setAbsentPresonNum(workshopAttendBoardPojo.getAbsentPresonNum()+1);
			}
			if(leaveNum>0) {
				//车间请假次数
				workshopAttendBoardPojo.setLeaveNum(workshopAttendBoardPojo.getLeaveNum()+leaveNum);
				//车间请假人次
				workshopAttendBoardPojo.setLeavePresonNum(workshopAttendBoardPojo.getLeavePresonNum()+1);
				//请假 最多次数人员
				if(leaveNum>=leaveTimesMaximum) {
					List<Map<String, Object>> leaveTimesMaximumPersons = attendBoard.getLeaveTimesMaximumPerson();
					leaveTimesMaximumPersons = AttendanceUtil.judgeMaximum(leaveNum, leaveTimesMaximum, leaveTimesMaximumPersons, userName);
					leaveTimesMaximum = leaveNum;
				}
				//请假时长最多人员
				if(leaveDuration>=leaveDurationMaximum) {
					List<Map<String, Object>> leaveDurationMaximumPersons = attendBoard.getLeaveDurationMaximumPerson();
					leaveDurationMaximumPersons = AttendanceUtil.judgeMaximum(leaveDuration, leaveDurationMaximum, leaveDurationMaximumPersons, userName);
					leaveDurationMaximum = leaveDuration;
				}
			}
			if(fillcardNum>0) {
				//车间补卡次数
				workshopAttendBoardPojo.setFillcardNum(workshopAttendBoardPojo.getFillcardPresonNum()+fillcardNum);
				//车间补卡人次
				workshopAttendBoardPojo.setFillcardPresonNum(workshopAttendBoardPojo.getFillcardPresonNum()+1);
				//补卡次数最多人员
				if(fillcardNum>=fillcardTimesMaximum) {
					List<Map<String, Object>> fillcardTimesMaximumPersons = attendBoard.getFillcardTimesMaximumPerson();
					fillcardTimesMaximumPersons = AttendanceUtil.judgeMaximum(fillcardNum, fillcardTimesMaximum, fillcardTimesMaximumPersons, userName);
					fillcardTimesMaximum = fillcardNum;
				}
			}
		}
		//车间今日出勤人数
		for(int i=0;i<workshopAttendBoardList.size();i++) {
			WorkshopAttendBoardPojo e = workshopAttendBoardList.get(i);
			WorkshopAttendPersonNumPojo e2 = workshopAttendPersonNumPojos.get(i);
			e.setTodayOndutyPersonNum(e2.getPersonNum());
		}
		return attendBoard;
	}
	
	/**
	 * 查询车间当天考勤情况看板 
	 * @param requestMap
	 * @throws ParseException
	 * @author 王荣
	 */
	public List<WorkshopCurrentDayAttendInfoBoardPojo> getCurrentDayAttendInfoBoard(Map<String, Object> requestMap) throws ParseException {
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
		requestMap.put("startDate", sdf.format(new Date()));
		requestMap.put("endDate", sdf.format(new Date()));
		
		List<WorkshopInfoPojo> list = attendanceMapper.getWorkshopAttendanceBoard(requestMap);
		List<WorkshopCurrentDayAttendInfoBoardPojo> resultList = new ArrayList<>();
		/* 遍历每个车间 */
		for(int index=0;index<list.size();index++) {
			/* 车间人员打卡信息 */
			WorkshopInfoPojo workshopInfoPojo = list.get(index);
			String deptName = workshopInfoPojo.getDeptName();
			String workshopName = workshopInfoPojo.getWorkshopName();
			
			List<AttendanceSummaryPojo> attendanceSummaryList = workshopInfoPojo.getAttendanceSummaryList();
			/* 遍历每个人 */
			for(AttendanceSummaryPojo pojo : attendanceSummaryList) {
				String groupName = pojo.getGroupName();
				int exceptionTimes = 0;
				//获取考勤组名，用于判断是否在考勤组内
				if(groupName==null) {
					//不在考勤组
					continue;
				}
				WorkshopCurrentDayAttendInfoBoardPojo currentDayAttendInfoPojo = new WorkshopCurrentDayAttendInfoBoardPojo();
				
				currentDayAttendInfoPojo.setDeptName(deptName);
				currentDayAttendInfoPojo.setWorkshopName(workshopName);
				currentDayAttendInfoPojo.setUserName(pojo.getUserName());
				List<Map<String, Object>> clockList = new ArrayList<>();
				List<QuerySummaryBodyWorkdatePojo> workdateList = pojo.getWorkdateList();
				/* 遍历每天 */
				for(int i = 0;i<workdateList.size();i++) {
					QuerySummaryBodyWorkdatePojo workdatePojo = workdateList.get(i);
					//班次名称
					String className = workdatePojo.getClassName();
					//当天请假信息
					List<Map<String, Object>> currentDayleaveList = workdateList.get(i).getCurrentDayleaveList();
					//当天打卡记录
					List<WorkdateClockInfo> clockInfoList = workdatePojo.getClockInfoList();
					if(className!=null) {
						//正常上班
						int j = 0;
						for(j=0;j<clockInfoList.size();j++) {
							WorkdateClockInfo clockInfo = clockInfoList.get(j);
							//基准打卡时间
							String baseCheckTime = clockInfo.getBaseCheckTime();
							DateTime dateTime1 = new DateTime(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(baseCheckTime));
							//实际打卡时间
							String userCheckTime = clockInfo.getUserCheckTime();
							//打卡结果
							String timeResult = clockInfo.getTimeResult();
							Map<String, Object> map = new HashMap<>();
							map.put("timeResult", timeResult);
							
							if(!DingTalkUtil.TIME_RESULT_NORMAL.equals(timeResult)) {
								
								if(DingTalkUtil.TIME_RESULT_LATE.equals(timeResult)) {
									exceptionTimes++;
									//迟到
									if(currentDayleaveList.size()>0) {
										for(Map<String, Object> leaveMap : currentDayleaveList) {
											String startTime = leaveMap.get("startTime").toString();
											String endTime = leaveMap.get("endTime").toString();
											if(TimeAndUnix.compareDate(startTime,userCheckTime)>=0
													&& TimeAndUnix.compareDate(userCheckTime,endTime)>0) {
												if(TimeAndUnix.compareDate(baseCheckTime,startTime)>0) {
													//如果基准时间在请假开始之前，计算基准时间到请假开始时间为迟到时长
													break;
												}else {
													//不计迟到次数,记为请假
													map.put("timeResult", DingTalkUtil.TIME_RESULT_LEAVE);
													break;
												}
											}
										}
									}
								}else if(DingTalkUtil.TIME_RESULT_EARLY.equals(timeResult)) {
									exceptionTimes++;
									//早退
									if(currentDayleaveList.size()>0) {
										//如果当天有请假单，判断是不是真的早退
										for(Map<String, Object> leaveMap : currentDayleaveList) {
											String startTime = leaveMap.get("startTime").toString();
											String endTime = leaveMap.get("endTime").toString();
											//早退打卡在请假范围内
											if(TimeAndUnix.compareDate(startTime,userCheckTime)>=0
													&& TimeAndUnix.compareDate(userCheckTime,endTime)>0) {
												if(TimeAndUnix.compareDate(endTime,baseCheckTime)>0) {
													break;
												}else {
													map.put("timeResult", DingTalkUtil.TIME_RESULT_LEAVE);
													break;
												}
											}
										}
									}
								}else if(DingTalkUtil.TIME_RESULT_LACK.equals(timeResult)) {
									exceptionTimes++;
									//缺卡
									if(currentDayleaveList.size()>0) {
										for(Map<String,Object> leaveMap : currentDayleaveList) {
											String startTime = leaveMap.get("startTime").toString();
											String endTime = leaveMap.get("endTime").toString();
											DateTime sTime = new DateTime(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(startTime));
											DateTime eTime = new DateTime(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(endTime));
											if((dateTime1.isAfter(sTime) || dateTime1.isEqual(sTime)) 
													&& (dateTime1.isBefore(eTime) || dateTime1.isEqual(eTime))) {
												//基础打卡时间在请假范围内，不计缺卡
												map.put("timeResult", DingTalkUtil.TIME_RESULT_LEAVE);
												break;
											}
										}
									}
								}
							}
							clockList.add(map);
						}
					}
				}
				if(exceptionTimes>0) {
					currentDayAttendInfoPojo.setClockList(clockList);
					resultList.add(currentDayAttendInfoPojo);
				}
			}
		}
		return resultList;
	}
	
	/**
	 * 获取未处理审核单，用于发送未处理审核信息
	 * @return {@code List<PendingApprovalInfoPojo>}
	 * @author 王荣
	 */
	public List<PendingApprovalInfoPojo> getPendingApprovalMessage(){
		return attendanceMapper.getPendingApprovalMessage();
	}
	
	/**
	 * 获取一个时间段内的考勤信息
	 * 时间段内的请假次数
	 * @author 王荣
	 * @return 
	 * @throws ParseException 
	 */
	public List<AttendanceInfoResultPojo> getAttendanceInfo(Map<String, Object> requestMap) throws ParseException {
		List<AttendanceInfoPojo> list = attendanceMapper.getAttendanceInfo(requestMap);
		assertThat("数据为空", list, notNullValue());
		List<AttendanceInfoResultPojo> attendanceInfoResultList = new ArrayList<>();
		//遍历每个人
		for(AttendanceInfoPojo pojo : list) {
			String groupName = pojo.getGroupName();
			String userName = pojo.getUserName();//用户名
			Integer userId = pojo.getUserId();//用户id
			//要返回的这个人的考勤信息
			AttendanceInfoResultPojo attendanceInfoResult = new AttendanceInfoResultPojo(userId,userName,groupName,pojo.getDeptName(),pojo.getWorkshopName(),pojo.getWorkerNo());
			attendanceInfoResult.setMethod(pojo.getMethod());
			//请假信息
			attendanceInfoResult.setLeaveList(pojo.getLeaveList());
			attendanceInfoResultList.add(attendanceInfoResult);
			
			//要返回的这个人的考勤信息的每日信息
			List<AttendanceDailyInfoPojo> attendanceDailyInfoList = new ArrayList<>();
			attendanceInfoResult.setAttendanceDailyInfoList(attendanceDailyInfoList);
			//每日打卡信息
			List<QuerySummaryBodyWorkdatePojo> workdateList = pojo.getWorkdateList();
			//出勤工资：每日工资++
			double attendanceSalary = 0;
			//迟到扣款
			Double lateDeductions = 0.0;
			//早退扣款
			Double earlyDeductions = 0.0;
			//旷工扣款
			Double absentDeductions = 0.0;
			//请假扣款
			Double leaveDeductions = 0.0;
			//加班工资
			Double overtimeSalary = 0.0;
			//月薪
			Double monthlySalary = 0.0;
			//加班规则信息
			AttendOvertimeRuleBean overtimeRuleBean = pojo.getOvertimeRuleBean();
			if(overtimeRuleBean==null) overtimeRuleBean = new AttendOvertimeRuleBean();
			//遍历每天的打卡数据
			for(QuerySummaryBodyWorkdatePojo workdatePojo : workdateList) {
				monthlySalary = workdatePojo.getMonthlySalary();
				attendanceSalary += workdatePojo.getRealSalary();
				lateDeductions += workdatePojo.getLateDeductions();
				earlyDeductions += workdatePojo.getEarlyDeductions();
				absentDeductions += workdatePojo.getAbsentDeductions();
				leaveDeductions += workdatePojo.getLeaveDeductions();
				overtimeSalary += workdatePojo.getOvertimeSalary();
				
				String className = workdatePojo.getClassName(),//班次名称
						workdate = workdatePojo.getWorkdate(),//日期
						attendanceTime = workdatePojo.getAttendanceTime();//打卡排班
				List<Map<String, Object>> effectiveTimeInfoList = new ArrayList<>(),//有效工时信息
					lateInfoList = new ArrayList<>(),//迟到信息
					earlyInfoList = new ArrayList<>(),//早退信息
					overtimeInfoList = new ArrayList<>(),//加班信息
					leaveList = new ArrayList<>(),//请假信息
					lackInfoList = new ArrayList<>();//缺卡信息
				boolean isNeedRest = workdatePojo.getIsNeedRest()==1;
				String restStart = null,restEnd = null;
				if(isNeedRest) {
					restStart = workdate+" "+workdatePojo.getRestStart()+":00";
					restEnd = workdate+" "+workdatePojo.getRestEnd()+":00";
				}
				//当日请假信息
				leaveList = workdatePojo.getCurrentDayleaveList();
				for(Map<String, Object> leaveMap : leaveList) {
					//新建一个list 放入map，并将map中的时间转化为long
					List<Map<String, Object>> currentDayleaveList = new ArrayList<>();
					currentDayleaveList.add(leaveMap);
					//计算当日内的请假时长并转换为long
					long duration = isNeedRest ? 
							DingTalkUtil.calculateLeaveDuration(currentDayleaveList, attendanceTime, workdate, restStart, restEnd)
							: DingTalkUtil.calculateLeaveDuration(currentDayleaveList, attendanceTime, workdate);
					leaveMap.put("duration", duration);
					leaveMap.put("startTime", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(leaveMap.get("startTime").toString()).getTime());
					leaveMap.put("endTime",new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(leaveMap.get("endTime").toString()).getTime());
				}
				/* 返回的考勤每日信息 */
				AttendanceDailyInfoPojo attendanceDailyInfo = new AttendanceDailyInfoPojo(workdate,className,attendanceTime,
						effectiveTimeInfoList,lateInfoList,earlyInfoList,overtimeInfoList,leaveList,lackInfoList);
				attendanceDailyInfoList.add(attendanceDailyInfo);
				//当日加班审批信息
				List<Map<String,Object>> currentDateOvertimeInfoList = workdatePojo.getOvertimeInfoList();
				//打卡信息
				List<WorkdateClockInfo> clockInfoList = workdatePojo.getClockInfoList();
				//休息时，即使不打卡也会有一条只有姓名和日期的记录，需要remove这条记录
				if(className==null && clockInfoList.get(0).getUserCheckTime()==null) clockInfoList.remove(0);
				//一天应打卡次数
				int clockTimes = attendanceTime==null?0:attendanceTime.replace(" ", "-").split("-").length;
				//过滤打卡记录,休息时checkType全为1，故不过滤
				if(className!=null) clockInfoList = AttendanceUtil.getClockList(clockInfoList, clockTimes,attendanceTime);
				//遍历每条打卡数据
				for(int clockIdx=0;clockIdx<clockInfoList.size();clockIdx++) {
					WorkdateClockInfo clockInfo = clockInfoList.get(clockIdx);
					String baseCheckTime = clockInfo.getBaseCheckTime(),//下班时间
							userCheckTime = clockInfo.getUserCheckTime(),//下班打卡时间
							timeResult = clockInfo.getTimeResult();//下班打卡结果
					long checkTime = 0;
					long baseTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(baseCheckTime).getTime();
					//忽略秒级精度
					if(userCheckTime!=null) {
						userCheckTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new SimpleDateFormat("yyyy-MM-dd HH:mm").parse(userCheckTime));
						checkTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(userCheckTime).getTime();
					}
					//下班 计算工作时长（减去请假时长）、加班时长、旷工时长
					if(clockIdx%2==1) {
						WorkdateClockInfo ondutyClockInfo = clockInfoList.get(clockIdx-1);
						//有效工作时长、旷工时长
						if(className!=null) {
							effectiveTimeInfoList.add(AttendanceUtil.getEffectiveTimeInfo(leaveList,
									ondutyClockInfo.getBaseCheckTime(), ondutyClockInfo.getUserCheckTime(), ondutyClockInfo.getTimeResult(),
									baseCheckTime, userCheckTime, timeResult));
						}
						//加班时长
						if(className==null || (clockIdx>clockTimes-1 && clockInfoList.size()>clockTimes)) {
							//休息加班 || 多打两次卡的加班
							Map<String,Object> overtimeMapTemp = AttendanceUtil.getOvertimeInfo(ondutyClockInfo.getUserCheckTime(), userCheckTime, overtimeRuleBean,currentDateOvertimeInfoList);
							if(overtimeMapTemp!=null) overtimeInfoList.add(overtimeMapTemp);
						}else if(clockIdx==clockTimes-1 && clockInfoList.size()==clockTimes) {//正常一天打卡不多不少 最后一次可能是连班
							//连班计算加班
							Map<String,Object> overtimeMapTemp = AttendanceUtil.getOvertimeInfo(baseCheckTime, userCheckTime, overtimeRuleBean,currentDateOvertimeInfoList);
							if(overtimeMapTemp!=null) overtimeInfoList.add(overtimeMapTemp);
						}
					}
					/* 迟到、早退、缺卡 */
					if(AttendanceUtil.TIME_RESULT_LATE.equals(timeResult)) {//迟到
						//获取迟到信息
						Map<String, Object> lateInfoMap = getLateInfo(leaveList, checkTime, baseTime, clockIdx);
						if(lateInfoMap.size()>0) lateInfoList.add(lateInfoMap);
					}else if(AttendanceUtil.TIME_RESULT_EARLY.equals(timeResult)) {//早退
						//获取早退信息
						Map<String , Object> earlyInfoMap = getEarlyInfo(leaveList, checkTime, baseTime, clockIdx);
						if(earlyInfoMap.size()>0) earlyInfoList.add(earlyInfoMap);
					}else if(AttendanceUtil.TIME_RESULT_LACK.equals(timeResult)) {//缺卡
						//获取缺卡信息
						Map<String, Object> lackInfoMap = getLackInfo(leaveList, baseTime, clockIdx);
						if(lackInfoMap.size()>0) lackInfoList.add(lackInfoMap);
					}
				}
			}
			attendanceInfoResult.setAttendanceSalary(attendanceSalary);
			attendanceInfoResult.setLateDeductions(lateDeductions);/** 迟到扣款	 */
			attendanceInfoResult.setEarlyDeductions(earlyDeductions);/** 早退扣款 */
			attendanceInfoResult.setAbsentDeductions(absentDeductions);/**旷工扣款*/
			attendanceInfoResult.setLeaveDeductions(leaveDeductions);/**请假扣款 */
			attendanceInfoResult.setOvertimeSalary(overtimeSalary);/**加班工资 */
			attendanceInfoResult.setMonthlySalary(monthlySalary);/*月薪*/
		}
		return attendanceInfoResultList;
	}
	
	/**
	 * 获取缺卡信息，size为0表示没有缺卡
	 * @param leaveList 当日请假信息
	 * @param baseTime 打卡时间
	 * @param clockIdx 打卡顺序 0,1,2,3...
	 * @author 王荣
	 * @throws ParseException 
	 */
	public static Map<String, Object> getLackInfo(List<Map<String, Object>> leaveList,long baseTime,int clockIdx) throws ParseException{
		Map<String, Object> lackInfoMap = new HashMap<>();
		boolean isLack = true;
		if(leaveList!=null && leaveList.size()>0) {
			for(Map<String, Object> leaveMap : leaveList) {
				long startTime = 0;
				long endTime = 0;
				//判断是否为数字类型，是则直接转long，否则转date再转long
				if(StringUtils.isNumeric(leaveMap.get("startTime").toString())) {
					startTime = Long.valueOf(leaveMap.get("startTime").toString());
					endTime = Long.valueOf(leaveMap.get("endTime").toString());
				}else {
					startTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(leaveMap.get("startTime").toString()).getTime();
					endTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(leaveMap.get("endTime").toString()).getTime();
				}
				//如果基准打卡时间在请假时间范围内时，不计缺卡
				if(baseTime>=startTime && endTime>=baseTime) {
					isLack = false;
					break;
				}
			}
		}
		if(isLack) {
			lackInfoMap.put("baseTime", baseTime);
			lackInfoMap.put("index", clockIdx);
		}
		return lackInfoMap;
	}
	
	/**
	 * 获取迟到信息，size为0表示没有迟到
	 * @param leaveList 当日请假信息
	 * @param checkTime 迟到打卡时间
	 * @param baseTime 打卡时间
	 * @param clockIdx 打卡顺序 0,1,2,3...
	 * @author 王荣
	 * @throws ParseException 
	 */
	public static Map<String, Object> getLateInfo(List<Map<String, Object>> leaveList,long checkTime,long baseTime,int clockIdx) throws ParseException{
		Map<String, Object> lateInfoMap = new HashMap<>();
		long lateDuration = 0;
		if(leaveList!=null && leaveList.size()>0) {
			for(int leaveIdx=0;leaveIdx<leaveList.size();leaveIdx++) {
				Map<String, Object> leaveMap = leaveList.get(leaveIdx);
				long startTime = 0;
				long endTime = 0;
				//判断是否为数字类型，是则直接转long，否则转date再转long
				if(StringUtils.isNumeric(leaveMap.get("startTime").toString())) {
					startTime = Long.valueOf(leaveMap.get("startTime").toString());
					endTime = Long.valueOf(leaveMap.get("endTime").toString());
				}else {
					startTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(leaveMap.get("startTime").toString()).getTime();
					endTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(leaveMap.get("endTime").toString()).getTime();
				}
				//迟到打卡时间在请假开始之前 则continue，如果是最后一次循环 则计算迟到时间
				if(startTime>checkTime) {
					if(leaveIdx==leaveList.size()-1) {
						lateDuration = checkTime - baseTime;
						break;
					}
					continue;
				}
				//迟到时间在请假时间范围内时
				if(checkTime>=startTime && endTime>=checkTime) {
					//如果基准时间在请假开始之前，计算基准时间到请假开始时间为迟到时长
					if(baseTime<startTime) {
						lateDuration = startTime - baseTime;
						break;
					}
					//打卡时间与基准时间在请假时间范围内，不计入迟到
					break;
				}else if(checkTime>endTime && baseTime>=startTime && baseTime<=endTime) {
					//打卡时间在请假结束时间之后，基准时间在请假时间范围内，计算请假结束时间到打卡时间为迟到时长
					lateDuration = checkTime-endTime;
					break;
				}else if(baseTime<startTime && checkTime>endTime) {
					//打卡时间在请假结束时间之后，基准时间在请假开始时间之前，计算基准时间到请假开始时间+请假结束时间到打卡时间为迟到时长
					lateDuration = startTime-baseTime;
					lateDuration += checkTime-endTime;
					break;
				}
			}
		}else {
			lateDuration = checkTime-baseTime;
		}
		if(lateDuration>0) {
			lateInfoMap.put("duration", lateDuration);
			lateInfoMap.put("baseTime", baseTime);
			lateInfoMap.put("checkTime", checkTime);
			lateInfoMap.put("index", clockIdx);
		}
		return lateInfoMap;
	}
	
	/**
	 * 获取早退信息，size为0表示没有早退
	 * @param leaveList 当日请假信息集合
	 * @param checkTime 早退打卡时间
	 * @param baseTime 打卡时间
	 * @param clockIdx 打卡顺序 0,1,2,3...
	 * @author 王荣
	 * @throws ParseException 
	 */
	public static Map<String , Object> getEarlyInfo(List<Map<String, Object>> leaveList,long checkTime,long baseTime,int clockIdx) throws ParseException {
		Map<String , Object> earlyInfoMap = new HashMap<>();
		long earlyDuration = 0;
		if(leaveList!=null && leaveList.size()>0) {
			for(int leaveIdx=0;leaveIdx<leaveList.size();leaveIdx++) {
				Map<String, Object> leaveMap = leaveList.get(leaveIdx);
				long startTime = 0;
				long endTime = 0;
				//判断是否为数字类型，是则直接转long，否则转date再转long
				if(StringUtils.isNumeric(leaveMap.get("startTime").toString())) {
					startTime = Long.valueOf(leaveMap.get("startTime").toString());
					endTime = Long.valueOf(leaveMap.get("endTime").toString());
				}else {
					startTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(leaveMap.get("startTime").toString()).getTime();
					endTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(leaveMap.get("endTime").toString()).getTime();
				}
				//如果请假结束时间在早退时间之前，则continue  如果是最后一次循环，则计算早退时间
				if(checkTime>endTime) {
					if(leaveIdx==leaveList.size()-1) {
						earlyDuration = baseTime-checkTime;
						break;
					}
					continue;
				}
				//早退时间在请假时间范围内
				if(checkTime>=startTime && endTime<=checkTime) {
					//但是请假结束时间在基准打卡时间之前
					if(baseTime>endTime) {
						//请假结束时间到基准打卡时间
						earlyDuration = baseTime-endTime;
						break;
					}
					break;
				}else if(startTime>checkTime && baseTime>=startTime && endTime>=baseTime) {
					//打卡时间在请假开始时间之前并且基准时间在请假时间范围内,计算打卡时间到请假开始时间的差值作为早退时长
					earlyDuration += startTime-checkTime;
					break;
				}else if(startTime>checkTime && baseTime>endTime) {
					//打卡时间在请假开始时间之前但基准时间在请假结束时间之后
					//计算打卡时间到请假开始时间的差值+请假结束时间到基准时间的差值作为早退时长
					earlyDuration += startTime-checkTime;
					earlyDuration += baseTime-endTime;
					break;
				}
			}
		}else {
			earlyDuration = baseTime-checkTime;
		}
		if(earlyDuration>0) {
			earlyInfoMap.put("duration", earlyDuration);
			earlyInfoMap.put("baseTime", baseTime);
			earlyInfoMap.put("checkTime", checkTime);
			earlyInfoMap.put("index", clockIdx);
		}
		return earlyInfoMap;
	}
	
	/**
	 * 计算工资，返回每日工资明细
	 * @param requestMap
	 * @author 王荣
	 * @throws ParseException 
	 */
	@SuppressWarnings("unchecked")
	public List<AttendDailySalaryDetailBean> calculateSalary(Map<String, Object> requestMap) throws ParseException {
		//获取考勤统计信息
		List<AttendanceInfoResultPojo> attendanceInfos = getAttendanceInfo(requestMap);
		DateTime dateTime = new DateTime(new SimpleDateFormat("yyyy-MM-dd").parse(requestMap.get("startDate").toString()));
		//因为测试方法用的immutablMap，所以改变参数要重建一个map
		Map<String, Object> map = new HashMap<>();
		for(String key : requestMap.keySet()) {
			map.put(key, requestMap.get(key));
		}
		//月初月末
		map.put("startDate",dateTime.dayOfMonth().withMinimumValue().toString("yyyy-MM-dd"));
		map.put("endDate", dateTime.dayOfMonth().withMaximumValue().toString("yyyy-MM-dd"));
		//获取排班与工资设定信息
		List<AttendPlanAndFinanceDetailPojo> planAndFinanceDetailPojos = attendanceMapper.getAttendPlanAndFinanceDetail(map);
		List<AttendDailySalaryDetailBean> attendDailySalaryDetailBeans = new ArrayList<>();
		//每人每天的工资明细设置信息
		for(int index = 0;index<planAndFinanceDetailPojos.size();index++) {
			AttendPlanAndFinanceDetailPojo planAndFinanceDetailPojo = planAndFinanceDetailPojos.get(index);
			Integer userId = planAndFinanceDetailPojo.getUserId(),
					groupId = planAndFinanceDetailPojo.getGroupId(),
					deptId = planAndFinanceDetailPojo.getDeptId();
			//月薪
			double monthlySalary = planAndFinanceDetailPojo.getMonthlySalary() == null ? 0 : planAndFinanceDetailPojo.getMonthlySalary();
			//考勤统计信息
			AttendanceInfoResultPojo attendanceInfoResultPojo = attendanceInfos.get(index);
			assertThat("匹配错误:userId不相同", userId, equalTo(attendanceInfoResultPojo.getUserId()));
			//每日考勤统计信息
			List<AttendanceDailyInfoPojo> attendanceDailyInfoPojos = attendanceInfoResultPojo.getAttendanceDailyInfoList();
			attendanceDailyInfoPojos = attendanceDailyInfoPojos == null ? new ArrayList<>() : attendanceDailyInfoPojos;
			//迟到早退扣款设定
			List<Map<String, Double>> punishList = planAndFinanceDetailPojo.getPunishList();
			//工资设定明细
			Map<String, Object> financeDetail = planAndFinanceDetailPojo.getFinanceDetailMap();
			double absentRatio = financeDetail == null || financeDetail.get("absentRatio")==null ? 
						1 : Double.valueOf(financeDetail.get("absentRatio").toString()),//旷工扣款比率 默认为1
					leaveRatio = financeDetail == null || financeDetail.get("leaveRatio")==null ? 
						1 : Double.valueOf(financeDetail.get("leaveRatio").toString()),//请假扣款比率
					overtimeRatio = financeDetail == null || financeDetail.get("overtimeRatio")==null ? 
						1 : Double.valueOf(financeDetail.get("overtimeRatio").toString());//加班工资比率
			//每日排班，用于计算月工作时长
			List<Map<String, Object>> workdateList = planAndFinanceDetailPojo.getWorkdateList();
			int monthWorkDays = 0;//足月工作天数
			double monthWorkTime = 0;//分钟
			for(Map<String, Object> workdateMap : workdateList) {
				//足月工作天数累加
				if(workdateMap.get("classId")!=null) monthWorkDays++;
				//当天的打卡列表
				List<String> clockList = (List<String>) workdateMap.get("clock");
				for(int clockIndex = 0;clockIndex<clockList.size();clockIndex++) {
					if(clockIndex%2==1) {
						String[] clockArr1 = clockList.get(clockIndex-1).split(":");
						String[] clockArr2 = clockList.get(clockIndex).split(":");
						//足月工作时长
						monthWorkTime += Integer.valueOf(clockArr2[0])*60+Integer.valueOf(clockArr2[1]) - (Integer.valueOf(clockArr1[0])*60+Integer.valueOf(clockArr1[1]));
					}
				}
			}
			//当日应得薪水=月薪*（本月入职天数/本月天数）/足月工作天数
			double dateSalary = monthlySalary*(Double.valueOf(workdateList.size())/dateTime.dayOfMonth().getMaximumValue())/monthWorkDays;
			//时薪=月薪*（本月入职天数/本月天数）/足月工作时长
			double hourlySalary = monthWorkTime == 0 ? 0 : monthlySalary*(Double.valueOf(workdateList.size())/dateTime.dayOfMonth().getMaximumValue())/(monthWorkTime/60);
			//每日考勤统计信息
			for(AttendanceDailyInfoPojo attendanceDailyInfoPojo : attendanceDailyInfoPojos) {
				String workdate = attendanceDailyInfoPojo.getWorkdate();
				String className = attendanceDailyInfoPojo.getClassName();
				Map<String, Double> attendDailyInfoMap = AttendanceUtil.getAttendanceDailyInfo(attendanceDailyInfoPojo, punishList,hourlySalary);
				Double effectiveTimeHours = attendDailyInfoMap.get("effectiveTimeHours"),
						absentTimeHours = attendDailyInfoMap.get("absentTimeHours"),
						lateDeduction = attendDailyInfoMap.get("lateDeduction"),
						earlyDeduction = attendDailyInfoMap.get("earlyDeduction"),
						leaveHours = attendDailyInfoMap.get("leaveHours"),
						overtimeHours = attendDailyInfoMap.get("overtimeHours");
				double effectiveSalary = effectiveTimeHours * hourlySalary;//有效工作薪水
				double absentDeductions = absentTimeHours * hourlySalary * absentRatio;//旷工扣款
				double leaveSalary = leaveHours * hourlySalary * leaveRatio;//请假扣款
				double overtimeSalary = overtimeHours * hourlySalary * overtimeRatio;//加班薪水
				double realSalary = 0;
				/*
				 * 小作用域的dateSalary 
				 * 当休息日不计工资且休息时，设当日应得薪水为0
				 */
				double tempDateSalary = className==null ? 0 : dateSalary;
				//实得薪水=当日应得薪水-旷工扣款-请假扣款+加班薪水-迟到扣款-早退扣款
				realSalary = tempDateSalary-absentDeductions-leaveSalary+overtimeSalary-lateDeduction-earlyDeduction;
				AttendDailySalaryDetailBean attendDailySalaryDetailBean = 
						new AttendDailySalaryDetailBean.Builder()
						.userId(userId).deptId(deptId).groupId(groupId).workdate(workdate)
						.monthlySalary(monthlySalary).dateSalary(tempDateSalary)
						.hourlySalary(hourlySalary).fullMonthlyWorkingHours(monthWorkTime/60)
						.effectiveHours(effectiveTimeHours).effectiveSalary(effectiveSalary)
						.lateDeductions(lateDeduction).earlyDeductions(earlyDeduction)
						.absentDeductions(absentDeductions).absentHours(absentTimeHours).absentRatio(absentRatio)
						.leaveDeductions(leaveSalary).leaveHours(leaveHours).leaveRatio(leaveRatio)
						.overtimeSalary(overtimeSalary).overtimeHours(overtimeHours).overtimeRatio(overtimeRatio)
						.realSalary(realSalary)
						.build();
				attendDailySalaryDetailBeans.add(attendDailySalaryDetailBean);
			}
		}
		return attendDailySalaryDetailBeans;
	}

	/**
	 * 通过每日工资明细记录中的月薪、工时计算并返回每日工资明细
	 * @param requestMap
	 * @return 每日工资明细集合
	 * @throws ParseException
	 * @author 王荣
	 */
	public List<AttendDailySalaryDetailBean> calculateSalaryByDailySalaryDetail(Map<String, Object> requestMap) throws ParseException{
		//考勤统计信息集合
		List<AttendanceInfoResultPojo> attendanceInfos = getAttendanceInfo(requestMap);
		//要返回的集合
		List<AttendDailySalaryDetailBean> attendDailySalaryDetailBeans = new ArrayList<>();
		//某人 某个时间段 的每日工资明细集合
		List<DailySalaryDetailByConditionPojo> dailySalaryDetailByConditionPojos = attendanceMapper.getDailySalaryDetailByCondition(requestMap);
		//获取排班与工资设定信息
		List<AttendPlanAndFinanceDetailPojo> planAndFinanceDetailPojos = attendanceMapper.getAttendPlanAndFinanceDetail(requestMap);
		//遍历每个人的考勤统计信息集合
		for(int index = 0;index<attendanceInfos.size();index++) {
			//某人的考勤统计信息
			AttendanceInfoResultPojo attendanceInfoResultPojo = attendanceInfos.get(index);
			//某人的每日统计信息集合
			List<AttendanceDailyInfoPojo> attendanceDailyInfoList = attendanceInfoResultPojo.getAttendanceDailyInfoList();
			//某人的每日工资明细信息
			List<DailySalaryDetailPojo> dailySalaryDetails = dailySalaryDetailByConditionPojos.get(index).getDailySalaryDetails();
			if(attendanceDailyInfoList==null || dailySalaryDetails==null) continue;
			Map<String, DailySalaryDetailPojo> dailySalaryDetailMap = new HashMap<>();
			//标识某人的每日统计信息集合与每日工资明细记录size是否相等
			boolean flag = attendanceDailyInfoList.size()!=dailySalaryDetails.size();
			//如果size不同,会出现IndexOutOfBoundsException,转换为map，根据key：workdate获取信息
			if(flag) {
				for(DailySalaryDetailPojo dailySalaryDetailPojo : dailySalaryDetails) {
					dailySalaryDetailMap.put(dailySalaryDetailPojo.getWorkdate(), dailySalaryDetailPojo);
				}
			}
			//迟到早退扣款设定信息
			List<Map<String, Double>> punishList = planAndFinanceDetailPojos.get(index).getPunishList();
			Integer userId = attendanceInfoResultPojo.getUserId();
			//遍历每日统计信息
			for(int j=0;j<attendanceDailyInfoList.size();j++) {
				//某日的考勤统计信息
				AttendanceDailyInfoPojo attendanceDailyInfoPojo = attendanceDailyInfoList.get(j);
				//日期
				String workdate = attendanceDailyInfoPojo.getWorkdate();
				//对应日期的每日工资明细记录
				DailySalaryDetailPojo dailySalaryDetail = null;
				if(flag) {
					//根据workdate获取信息
					dailySalaryDetail = dailySalaryDetailMap.get(workdate);
					//如果为null表示没有这一天的每日工资明细
					if(dailySalaryDetail==null) continue;
				}else {
					dailySalaryDetail = dailySalaryDetails.get(j);
				}
				double dateSalary = dailySalaryDetail.getDateSalary();
				double hourlySalary = dailySalaryDetail.getHourlySalary();
				Map<String, Double> attendDailyInfoMap = AttendanceUtil.getAttendanceDailyInfo(attendanceDailyInfoPojo, punishList,hourlySalary);
				Double effectiveTimeHours = attendDailyInfoMap.get("effectiveTimeHours"),
						absentTimeHours = attendDailyInfoMap.get("absentTimeHours"),
						lateDeduction = attendDailyInfoMap.get("lateDeduction"),
						earlyDeduction = attendDailyInfoMap.get("earlyDeduction"),
						leaveHours = attendDailyInfoMap.get("leaveHours"),
						overtimeHours = attendDailyInfoMap.get("overtimeHours");
				double effectiveSalary = effectiveTimeHours * hourlySalary;//有效工作薪水
				double absentDeductions = absentTimeHours * hourlySalary * dailySalaryDetail.getAbsentRatio();//旷工扣款
				double leaveSalary = leaveHours * hourlySalary * dailySalaryDetail.getLeaveRatio();//请假扣款
				double overtimeSalary = overtimeHours * hourlySalary * dailySalaryDetail.getOvertimeRatio();//加班薪水
				double realSalary = 0;
				dateSalary = attendanceDailyInfoPojo.getClassName()==null ? 0 : dateSalary;
				//实得薪水=当日应得薪水-旷工扣款-请假扣款+加班薪水-迟到扣款-早退扣款
				realSalary = dateSalary-absentDeductions-leaveSalary+overtimeSalary-lateDeduction-earlyDeduction;
				AttendDailySalaryDetailBean attendDailySalaryDetailBean = 
						new AttendDailySalaryDetailBean.Builder()
						.userId(userId).workdate(workdate)
						.effectiveHours(effectiveTimeHours).effectiveSalary(effectiveSalary)
						.lateDeductions(lateDeduction).earlyDeductions(earlyDeduction)
						.absentDeductions(absentDeductions).absentHours(absentTimeHours)
						.leaveDeductions(leaveSalary).leaveHours(leaveHours)
						.overtimeSalary(overtimeSalary).overtimeHours(overtimeHours)
						.realSalary(realSalary)
						.build();
				attendDailySalaryDetailBeans.add(attendDailySalaryDetailBean);
			}
		}
		return attendDailySalaryDetailBeans;
	}
	
	/**
	 * 保存考勤每日工资明细记录
	 * @param attendDailySalaryDetailBeans
	 * @author 王荣
	 */
	public void saveAttendDailySalaryDetail(List<AttendDailySalaryDetailBean> attendDailySalaryDetailBeans) {
		/* 当插入数据过多时，会报错 ：
		 * Cause: com.microsoft.sqlserver.jdbc.SQLServerException: 传入的表格格式数据流(TDS)远程过程调用(RPC)协议流不正确。此 RPC 请求中提供了过多的参数。最多应为 2100。
		 * 所以分批插入
		 * */
		/*List<AttendDailySalaryDetailBean> newList = new ArrayList<>();
		for (AttendDailySalaryDetailBean attendDailySalaryDetailBean : attendDailySalaryDetailBeans) {
			if (attendDailySalaryDetailBean.getWorkdate() != null ) {
				newList.add(attendDailySalaryDetailBean);
			}
		}
		attendDailySalaryDetailBeans = newList;*/
        if (attendDailySalaryDetailBeans != null && attendDailySalaryDetailBeans.size() > 0) {
            if (attendDailySalaryDetailBeans.size() > 80) {
            	List<AttendDailySalaryDetailBean> childrenList = null;
                int count = (int) Math.floor(attendDailySalaryDetailBeans.size() / 80);
                for (int i = 0; i < count; i++) {
                    childrenList = attendDailySalaryDetailBeans.subList(i * 80, (i + 1) * 80);
                    attendanceMapper.saveAttendDailySalaryDetail(childrenList);
                }
                // 防止下标越界
                childrenList = attendDailySalaryDetailBeans.subList(count * 80, attendDailySalaryDetailBeans.size());
                if(childrenList.size()>0)
                	attendanceMapper.saveAttendDailySalaryDetail(childrenList);
            } else {
                attendanceMapper.saveAttendDailySalaryDetail(attendDailySalaryDetailBeans);
            }
        }
	}
	
	/**
	 * 添加每日工资记录
	 * @param startDate 开始日期
	 * @param endDate 结束日期
	 * @param personId 用户id
	 * @param company 公司
	 * @author 王荣
	 * @throws ParseException 
	 */
	public void insertDailySalaryRecord(String startDate,String endDate,Integer personId,String company) throws ParseException {
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("startDate", startDate);
        map.put("endDate", endDate);
        map.put("personId", personId);
        map.put("loginCompany", company);
        List<AttendDailySalaryDetailBean> attendDailySalaryDetailBeans = calculateSalary(map);
        saveAttendDailySalaryDetail(attendDailySalaryDetailBeans);
    }
	
	/**
	 * 通过现在的月薪、工作排班修改每日工资明细
	 * @param startDate 开始日期
	 * @param endDate 结束日期
	 * @param company 公司
	 * @param personId 用户id
	 * @param groupId 考勤组id
	 * @author 王荣
	 */
	public void updateDailySalaryRecord(String startDate,String endDate,String company,Integer personId,Integer groupId) {
		Map<String, Object> requestMap = new HashMap<>();
		requestMap.put("startDate", startDate);
		requestMap.put("endDate", endDate);
		requestMap.put("loginCompany", company);
		requestMap.put("personId", personId);
		requestMap.put("groupId", groupId);
		try {
			//修改工资时，需要参数：生效日期startDate，personId
			//请假、补卡审批通过时，需要参数：请假补卡日期startDate-endDate，personId
			//修改考勤组工作日时，需要参数：本月开始结束日期，修改的考勤组id
			List<AttendDailySalaryDetailBean> salaryDetailBeans = calculateSalary(requestMap);
			updateDailySalaryRecord(salaryDetailBeans);
		} catch (ParseException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * 通过记录里的月薪、工时修改每日工资明细
	 * @param startDate 开始日期
	 * @param endDate 结束日期
	 * @param company 公司
	 * @param personId 用户id
	 * @author 王荣
	 */
	public void updateDailySalaryRecord(String startDate,String endDate,String company,Integer personId) {
		Map<String, Object> requestMap = new HashMap<>();
		requestMap.put("startDate", startDate);
		requestMap.put("endDate", endDate);
		requestMap.put("loginCompany", company);
		requestMap.put("personId", personId);
		List<AttendDailySalaryDetailBean> salaryDetailBeans = null;
		try {
			salaryDetailBeans = calculateSalaryByDailySalaryDetail(requestMap);
		} catch (ParseException e) {
			e.printStackTrace();
		}
		updateDailySalaryRecord(salaryDetailBeans);
	}
	
	/**
	 * 修改每日工资明细
	 * @param salaryDetailBeans 要修改的工资明细集合
	 * @author 王荣
	 */
	public void updateDailySalaryRecord(List<AttendDailySalaryDetailBean> salaryDetailBeans) {
		if (salaryDetailBeans != null && salaryDetailBeans.size() > 0) {
            if (salaryDetailBeans.size() > 80) {
            	List<AttendDailySalaryDetailBean> childrenList = null;
                int count = salaryDetailBeans.size() / 80;
                for (int i = 0; i < count; i++) {
                    childrenList = salaryDetailBeans.subList(i * 80, (i + 1) * 80);
                    attendanceMapper.updateAttendDailySalaryDetail(childrenList);
                }
                // 防止下标越界
                childrenList = salaryDetailBeans.subList(count * 80, salaryDetailBeans.size());
                attendanceMapper.updateAttendDailySalaryDetail(childrenList);
            } else {
            	attendanceMapper.updateAttendDailySalaryDetail(salaryDetailBeans);
            }
        }
	}
	
	/**
	 * 修改工资定时任务
	 * @author 王荣
	 */
	public void updateSalaryTimerTask() {
		attendanceMapper.updateSalaryTimerTask();
	}
	
	/**
	 * 每月初 定时保存上月的员工工资明细设定记录
	 * 复制并生成新的工资明细，但日期不同
	 * @author 王荣
	 */
	public void saveFinanceDetailByMonth() {
		attendanceMapper.saveFinanceDetailByMonth();
	}
	
	/**
	 * 获取某人某月的每日工资明细
	 * @param requestMap
	 * @author 王荣
	 */
	public List<DailySalaryDetailPojo> getDailySalaryDetail(Map<String, Object> requestMap) {
		List<DailySalaryDetailPojo> dailySalaryDetailPojos = attendanceMapper.getDailySalaryDetail(requestMap);
		return dailySalaryDetailPojos;
	}
	
	/**
	 * 获取部门与车间，用于手机查看员工出勤统计的搜索
	 * @param requestMap
	 * @author 王荣
	 */
	public List<DeptAndWorkshopPojo> getDeptAndWorkshop(Map<String, Object> requestMap){
		return attendanceMapper.getDeptAndWorkshop(requestMap);
	}
	
	/**
	 * 通过部门车间获取月统计信息 
	 * @param map
	 * @throws ParseException
	 * @author 王荣
	 */
	public DatatablesViewPageBean<AttendanceMonthSummaryBean> getMonthSummaryByDeptAndWorkshop(Map<String, Object> map) throws ParseException {
		//如果为空则设为"[]"
		map.put("deptIdList", DingTalkUtil.parseJsonArrayStrToListInteger((String) map.computeIfAbsent("deptIds", k -> "[]")));
		map.put("userIdList", DingTalkUtil.parseJsonArrayStrToListInteger((String) map.computeIfAbsent("userIds", k -> "[]")));
		map.put("workshopIdList", DingTalkUtil.parseJsonArrayStrToListInteger((String) map.computeIfAbsent("workshopIds", k -> "[]")));
        int count = dingTalkMapper.queryClockTimeCount(map);
        DatatablesViewPageBean<AttendanceMonthSummaryBean> dvpb = new DatatablesViewPageBean<AttendanceMonthSummaryBean>();
        dvpb.setiTotalDisplayRecords(count);
        dvpb.setiTotalRecords(count);
        if(count<=0) {
			dvpb.setReturnData(new ArrayList<AttendanceMonthSummaryBean>());
			return dvpb;
		}
        //查询开始结束日期大于今日时，设为今日日期
        if(new SimpleDateFormat("yyyy-MM-dd").parse(map.get("endDate").toString()).getTime() > new Date().getTime()) map.put("endDate", new SimpleDateFormat("yyyy-MM-dd").format(new Date()));
        if(new SimpleDateFormat("yyyy-MM-dd").parse(map.get("startDate").toString()).getTime() > new Date().getTime()) map.put("startDate", new SimpleDateFormat("yyyy-MM-dd").format(new Date()));
        List<AttendanceMonthSummaryBean> attendanceMonthSummaryBeans = new ArrayList<>();
        //考勤统计信息集合
        List<AttendanceInfoResultPojo> attendanceInfoResultPojos = getAttendanceInfo(map);
        //用户考勤工资明细
  		List<UserFinancePojo> userFinancePojos = dingTalkMapper.getUserFinanceDetail(map);
  		
  		//个人水电费用
  		Map<String, PersonOfDormStatisticsOut> personElectricAndWaterMap = dingTalkService.getPersonElectricAndWaterInfo(map.get("startDate").toString(), 
				new DateTime(new SimpleDateFormat("yyyy-MM-dd").parse(map.get("endDate").toString())).plusDays(1).toString("yyyy-MM-dd"), 
				map.get("loginCompany").toString());
		//个人计件工资
		map.put("startDay", map.get("startDate"));
		map.put("date", map.get("endDate"));
		map.put("workShop", "");
		Map<String, TableData> pieceWageMap = dingTalkService.getPieceWageInfo(map);
		//补料扣款
		Map<Integer, Double> applyRepairMoneyMap = dingTalkService.getApplyRepairMoneyInfo(map.get("startDate").toString(), map.get("endDate").toString(), map.get("loginCompany").toString());
  		
        for(int index=0;index<attendanceInfoResultPojos.size();index++) {
        	//某人的考勤统计信息
        	AttendanceInfoResultPojo attendanceInfoResultPojo = attendanceInfoResultPojos.get(index);
        	String userName = attendanceInfoResultPojo.getUserName();
        	Integer userId = attendanceInfoResultPojo.getUserId();
        	String method = attendanceInfoResultPojo.getMethod();
        	//每日统计信息
        	List<AttendanceDailyInfoPojo> attendanceDailyInfoPojos = attendanceInfoResultPojo.getAttendanceDailyInfoList();
        	//某人的考勤工资明细：社保公积金、其他扣款、其他加项
        	UserFinancePojo userFinancePojo = userFinancePojos.get(index);
        	long lateDuration = 0,earlyDuration = 0,leaveDuration = 0,overtimeDuration = 0,effectiveDuration = 0;
        	//个人水电费用信息
        	PersonOfDormStatisticsOut personElectricAndWater = personElectricAndWaterMap.get(userName);
			//电费
			double personElectricFee = personElectricAndWater==null ? 0 : personElectricAndWater.getElectricFee().doubleValue();
			//水费
			double personWaterFee = personElectricAndWater==null ? 0 : personElectricAndWater.getWaterFee().doubleValue();
			//计件工资
			double pieceWage = pieceWageMap.get(userName)==null ? 0 : pieceWageMap.get(userName).getSumPrice();
			//补料扣款
			double applyRepairMoney = applyRepairMoneyMap.get(userId)==null ? 0 : applyRepairMoneyMap.get(userId);
			
        	if(attendanceDailyInfoPojos!=null) {
        		//遍历每日统计信息，统计迟到、早退、请假、加班、工作时长
        		for(AttendanceDailyInfoPojo attendanceDailyInfoPojo : attendanceDailyInfoPojos) {
        			//当日迟到信息
        			List<Map<String, Object>> lateInfoList = attendanceDailyInfoPojo.getLateInfoList();
        			for(Map<String, Object> iMap : lateInfoList) {
        				lateDuration += Long.valueOf(iMap.get("duration").toString());
        			}
        			//早退信息
        			List<Map<String, Object>> earlyInfoList = attendanceDailyInfoPojo.getEarlyInfoList();
        			for(Map<String, Object> iMap : earlyInfoList) {
        				earlyDuration += Long.valueOf(iMap.get("duration").toString());
        			}
        			//请假信息
        			List<Map<String, Object>> leaveInfoList = attendanceDailyInfoPojo.getLeaveInfoList();
        			for(Map<String, Object> iMap : leaveInfoList) {
        				leaveDuration += Long.valueOf(iMap.get("duration").toString());
        			}
        			//加班信息
        			List<Map<String, Object>> overtimeInfoList = attendanceDailyInfoPojo.getOvertimeInfoList();
        			for(Map<String, Object> iMap : overtimeInfoList) {
        				//overtimeDuration += Long.valueOf(iMap.get("duration").toString());
        				overtimeDuration += Long.parseLong(iMap.get("duration").toString())/Long.parseLong(30*60*1000+"")*Long.parseLong(30*60*1000+"");
        			}
        			//有效工作时长信息
        			List<Map<String, Object>> effectiveTimeInfoList = attendanceDailyInfoPojo.getEffectiveTimeInfoList();
        			for(Map<String, Object> iMap : effectiveTimeInfoList) {
        				effectiveDuration += Long.valueOf(iMap.get("duration").toString());
        			}
        		}
        	}
        	double realSalary = 0;
        	double attendanceSalary = attendanceInfoResultPojo.getAttendanceSalary();
        	/** 迟到扣款	 */
    		double lateDeductions = attendanceInfoResultPojo.getLateDeductions();
    		/** 早退扣款 */
    		double earlyDeductions = attendanceInfoResultPojo.getEarlyDeductions();
    		/**旷工扣款*/
    		double absentDeductions = attendanceInfoResultPojo.getAbsentDeductions();
    		/**请假扣款 */
    		double leaveDeductions = attendanceInfoResultPojo.getLeaveDeductions();
    		/**加班工资 */
    		double overtimeSalary = attendanceInfoResultPojo.getOvertimeSalary();
    		/*月薪*/
    		double mothlySalary = attendanceInfoResultPojo.getMonthlySalary();
    		
    		pieceWage = pieceWage - lateDeductions - earlyDeductions - absentDeductions;

			if("按月".equals(method)) {
				realSalary = attendanceSalary;
				pieceWage = 0;
			}else if("计件".equals(method)) {
				realSalary = pieceWage;
				attendanceSalary = 0;
			}else {//保底加计件 计件工资>出勤工资 则用计件工资计算
				realSalary = pieceWage > attendanceSalary ? pieceWage : attendanceSalary;
			}
        	//实际薪水=出勤工资-社保-公积金-其他减项+其他加项
			realSalary = AttendanceUtil.getRealSalary(realSalary, 
					userFinancePojo.getOtherAddition(),
					userFinancePojo.getSocialSecurity(), 
					userFinancePojo.getAccumulationFund(), 
					userFinancePojo.getOtherDeductions(),
					personElectricFee,personWaterFee,applyRepairMoney);
        	AttendanceMonthSummaryBean attendanceMonthSummaryBean = new AttendanceMonthSummaryBean.Builder()
        			.userId(attendanceInfoResultPojo.getUserId())
        			.userName(attendanceInfoResultPojo.getUserName())
        			.groupName(attendanceInfoResultPojo.getGroupName())
        			.deptName(attendanceInfoResultPojo.getDeptName())
					.lateDuration(lateDuration)
					.earlyDuration(earlyDuration)
					.effectiveDuration(effectiveDuration)
					.leaveDuration(leaveDuration)
					.overtimeDuration(overtimeDuration/Long.parseLong(30*60*1000+"")*Long.parseLong(30*60*1000+""))
					.attendanceSalary(attendanceInfoResultPojo.getAttendanceSalary())
					.socialSecurity(userFinancePojo.getSocialSecurity())
					.accumulationFund(userFinancePojo.getAccumulationFund())
					.otherAddition(userFinancePojo.getOtherAddition())
					.otherDeduction(userFinancePojo.getOtherDeductions())
					.realSalary(realSalary)
					.applyRepairMoney(applyRepairMoney).electricFee(personElectricFee)
					.waterFee(personWaterFee).pieceWage(pieceWage)
					.method(method)
					.build();
        	
        	
        	//计算该月的奖罚 20190920
    		for(AttendanceDailyInfoPojo attendanceDailyInfoPojo : attendanceDailyInfoPojos) {
    			if(attendanceDailyInfoPojo.getWorkdate()==null) {
    				break;
    			}
    			//获取年月 201905 
    			String workdate = attendanceDailyInfoPojo.getWorkdate().substring(0,7).replace("-", "");
    			//计算这个月奖罚
    			Double award= dingTalkMapper.getViewAdministrativePenalty(userId,workdate,6);
    			Double punish= dingTalkMapper.getViewAdministrativePenalty(userId,workdate,5);
    			attendanceMonthSummaryBean.setAdministrativePenaltyForAward((award!=null)?award:0);
    			attendanceMonthSummaryBean.setAdministrativePenaltyForPunish((punish!=null)?punish:0);
    			realSalary=realSalary+attendanceMonthSummaryBean.getAdministrativePenaltyForAward()-attendanceMonthSummaryBean.getAdministrativePenaltyForPunish();
    			attendanceMonthSummaryBean.setRealSalary(realSalary);//实发工资
    			break;
    		}
    		
        	attendanceMonthSummaryBeans.add(attendanceMonthSummaryBean);
        }
        dvpb.setReturnData(attendanceMonthSummaryBeans);
		return dvpb;
	}

	public List<Map<String, Object>> getWorkHourForEverDay(Map<String, Object> requestMap) throws ParseException {
		List<Map<String, Object>> returnList = new ArrayList<>();
		DateTime dateTime = new DateTime(new SimpleDateFormat("yyyy-MM-dd").parse(requestMap.get("startDate").toString()));
		List<AttendPlanAndFinanceDetailPojo> planAndFinanceDetailPojos = attendanceMapper.getAttendPlanAndFinanceDetail(requestMap);
		for(int index = 0;index<planAndFinanceDetailPojos.size();index++) {
			AttendPlanAndFinanceDetailPojo planAndFinanceDetailPojo = planAndFinanceDetailPojos.get(index);
			requestMap.put("startDate",dateTime.dayOfMonth().withMinimumValue().toString("yyyy-MM-dd"));
			requestMap.put("endDate", dateTime.dayOfMonth().withMaximumValue().toString("yyyy-MM-dd"));
			//每日排班，用于计算月工作时长
			List<Map<String, Object>> workdateList = planAndFinanceDetailPojo.getWorkdateList();
			
			//int monthWorkDays = 0;//足月工作天数
			//double monthWorkTime = 0;//分钟
			
			for(Map<String, Object> workdateMap : workdateList) {
				//足月工作天数累加
				//if(workdateMap.get("classId")!=null) monthWorkDays++;
				//当天的打卡列表
				List<String> clockList = (List<String>) workdateMap.get("clock");
				String workdate =  workdateMap.get("workdate").toString();
				double dayWorkTime = 0 ;//每天工作时长 分钟
				for(int clockIndex = 0;clockIndex<clockList.size();clockIndex++) {
					if(clockIndex%2==1) {
						String[] clockArr1 = clockList.get(clockIndex-1).split(":");
						String[] clockArr2 = clockList.get(clockIndex).split(":");
						//足月工作时长
						dayWorkTime += Integer.valueOf(clockArr2[0])*60+Integer.valueOf(clockArr2[1]) - (Integer.valueOf(clockArr1[0])*60+Integer.valueOf(clockArr1[1]));
					}
				}
				Map map = new HashMap<>();
				map.put("dayWorkTime", dayWorkTime);
				map.put("workdate", workdate);
				map.put("userId", planAndFinanceDetailPojo.getUserId());
				map.put("userName", planAndFinanceDetailPojo.getUserName());
				returnList.add(map);
			}
		}
		return returnList;
	}
	
}
